<template>
    <view class="flex " :class=" [props.direction=='vertical'?'flex-row':'',_disabled?'opacity-6':'']">
        
        <view class="relative flex flex-col " :class="[props.direction=='horizontal'?'flex-col-center-start':'flex-row-center-center']" :style="[
        
        {width:_sliderBarCssWidth+'rpx',height:_sliderBarCssHeight+'rpx'}
        
        ]">
            <tm-sheet noLevel :round="10" :color="props.bgColor" 
            :height="props.direction=='horizontal'?props.height:_sliderBarCssHeight" 
            :width="props.direction=='horizontal'?_sliderBarCssWidth:props.height" :margin="[0,0]" 
            :padding="[0,0]"></tm-sheet>
            <slider-bar :followTheme="props.followTheme" :class="[props.direction=='horizontal'?'flex-col-center-start':'flex-row-center-center']" 
            :direction="props.direction" :color="props.color" :size="props.height" :x="_barLet" 
            :width="_barWidth"></slider-bar>
            <slider-mask :followTheme="props.followTheme" :color="props.color"  v-if="props.showLabel" :size="props.buttonSize" :step="props.step" :min="props.min" :max="props.max" :width="_sliderBarCssWidth"  :height="_sliderBarCssHeight" :direction="props.direction"></slider-mask>
            <slider-button :followTheme="props.followTheme"  :direction="props.direction" ref="btn0" :color="props.color" :x="btnPos[0].x"  @movestart="butnMoveStart($event,0)" @moveing="butnMove($event,0)" @moveend="butnMoveEnd($event,0)" :size="props.buttonSize"></slider-button>
            <slider-button  :followTheme="props.followTheme" :direction="props.direction" v-if="isDablue" ref="btn1" :color="props.color" :x="btnPos[1].x"  @movestart="butnMoveStart($event,1)" @moveing="butnMove($event,1)" @moveend="butnMoveEnd($event,1)" :size="props.buttonSize"></slider-button>
            
        </view>
        <view v-if="props.showLabel" :class=" [props.direction=='vertical'?'flex-col':'flex-row']"  >
            <slider-label :size="props.buttonSize" :step="props.step" :min="props.min" :max="props.max" 
            :width="_sliderBarCssWidth"  :height="_sliderBarCssHeight" :direction="props.direction"></slider-label>
        </view>
        <view v-if="showDetail" class="flex  absolute" :class="[props.direction=='horizontal'?'flex-col flex-col-start-center':' flex-row ']" :style="[
        props.direction=='horizontal'?{width:(props.width+props.buttonSize)+'rpx'}:{height:(props.width+props.buttonSize)+'rpx'}
        ]">
            <view 
            :class="[isNvue?'fixed':'absolute ',
            props.direction=='horizontal'?(isNvue?'t-0':'b-0'):'t-0 ',
            ]"
            class="mb-0  flex flex-col flex-col-bottom-center"  
            :style="[
            !isNvue?
            ( props.direction=='horizontal'?{transform:`translateX(${btnPos[BtnIndex].x}px)`,left:-(100-props.buttonSize+24)/2+'rpx'}:{transform:`translateY(${btnPos[BtnIndex].x}px)`,top:'-70rpx',left:-(100-props.buttonSize+24)/2+'rpx'})
            :
            {left:`${nvueDetailPos.left}px`,top:`${nvueDetailPos.bottom}px`}
            ]"
            >
                <tm-sheet _class="flex-center" color="grey-darken-5" :border="2" :margin="[0,0]" :padding="[10,6]" :width="100" :round="3">
                    <tm-text :label="_value"></tm-text>
                </tm-sheet>
                <tm-icon  color="grey-darken-5" _class="t--10" :font-size="32" name="tmicon-sort-down"></tm-icon>
            </view>
        </view>
    </view>
</template>
<script lang="ts" setup>
	/**
	 * 滑块
	 * description 注意，如果想开启单滑块（默认），default-value=0单个值即可，如果想双滑块，值等于数组比如:=[0,5]
	 */
import {computed, ref,Ref,toRaw,getCurrentInstance, watchEffect, watch,inject} from "vue"
import { inputPushItem, rulesItem } from "./../tm-form-item/interface"
import sliderBar from "./slider-bar.vue"
import sliderButton from "./slider-button.vue"
import tmSheet from '../tm-sheet/tm-sheet.vue';
import tmText from "../tm-text/tm-text.vue";
import TmIcon from "../tm-icon/tm-icon.vue";
import sliderLabel from "./slider-label.vue";
import sliderMask from "./slider-mask.vue";
// #ifdef APP-PLUS-NVUE
const dom = uni.requireNativePlugin('dom')
// #endif
const btn0 = ref<InstanceType<typeof sliderButton> | null>(null)
const btn1 = ref<InstanceType<typeof sliderButton> | null>(null)

const proxy = getCurrentInstance()?.proxy??null;
const emits = defineEmits(["update:modelValue",'change'])
const props = defineProps({
	//是否跟随全局主题的变换而变换
	followTheme: {
		type: [Boolean, String],
		default: true
	},
    //竖向时这个是高度。
    width:{
        type:Number,
        default:500
    },
    // 条的高度
    //竖向时，这个是宽度。
    height:{
        type:Number,
        default:8
    },
    //按钮的大小。
    buttonSize:{
        type:Number,
        default:46
    },
    /**
     * 方向
     * horizontal:水平,
     * vertical:竖向。
     */
    direction:{
        type:String,
        default:"horizontal"
    },
    color:{
        type:String,
        default:'primary'
    },
    bgColor:{
        type:String,
        default:'grey-3'
    },
    max:{
       type:Number,
        default:100 
    },
    min:{
       type:Number,
        default:0 
    },
    //默认的值一定要和v-model相同的类型，
    defaultValue:{
        type:[Array,Number],
        default:0
    },

    //双向绑定数据可以使用v-model,记住格式一定要和defaultValue相同。
    modelValue:{
        type:[Array,Number],
        default:0
    },
    //格式为自己的标签显示 ，是按钮上方的显示 标签。
    formart:{
        type:Function,
        default:()=>{
            return (val:number|string)=> {
                return val;
            }
        }
    },
    //只有这个为true,下面的step才会显示标签。
    showLabel:{
        type:Boolean,
        default:false
    },
    //需要显示的步骤标签。
    step:{
        type:Number,
        default:5
    },
    disabled:{
        type:Boolean,
        default:false
    },
})

interface btnMovetype {
    x:number,
    y:number
}
const width = computed(()=>uni.upx2px(props.width))
const isDarg  = ref(false)
const _disabled = computed(()=>props.disabled)
const btnPos:Ref<Array<btnMovetype>> = ref([
    {
        x:0,y:0
    },
    {
        x:0,y:0
    }
])
let _x = 0;
let _y = 0;
let buttonStaticsLeft = 0;
let buttonStaticsMaxLeft =  width.value;
const isNvue = ref(false)
const nvueDetailPos = ref({left:0,bottom:0})
// #ifdef APP-NVUE
isNvue.value=true;
// #endif

const _sliderBarCssWidth = computed(()=>{

    if(props.direction=='horizontal') return props.width+props.buttonSize
    return props.buttonSize 
})
const _sliderBarCssHeight = computed(()=>{
    if(props.direction=='horizontal') return props.buttonSize 
    return props.width+props.buttonSize
})

//当前操作的是哪个按钮。
const BtnIndex = ref(0)
//显示上访值。
const showDetail = ref(false)
const _valueMax = computed(()=>{
    return props.max-props.min;
})
const _barWidth = computed(()=>{
    return Math.abs(btnPos.value[0].x - btnPos.value[1].x)
})
const _barLet = computed(()=>{
    return Math.min(Math.abs(btnPos.value[0].x ), Math.abs(btnPos.value[1].x))
})

const _value = ref(0);
//是双滑块，还是单滑块。
const isDablue = ref(false)
if(typeof props.defaultValue == 'object' && Array.isArray(props.defaultValue)){
    isDablue.value = true;
}
zhuanghuaValue();
watchEffect(()=>{
    let val = Math.ceil(Math.abs(btnPos.value[BtnIndex.value].x)/uni.upx2px(props.width) * _valueMax.value + props.min);
    if (typeof props.formart === 'function') {
        
        let p = props.formart(val);
        if(typeof p === 'function'){
            p = p(val);
        }
        val = p;
    }
    _value.value = val;
})
emits("update:modelValue",getValue())
watch(()=>props.modelValue,()=>{
    if(!isDablue.value){
        btnPos.value[0].x = Math.abs(Number(props.modelValue) / _valueMax.value * uni.upx2px(props.width))
    }else{
        btnPos.value[0].x = Math.abs(Number(props.modelValue[0]) / _valueMax.value * uni.upx2px(props.width))
        btnPos.value[1].x = Math.abs(Number(props.modelValue[1]) / _valueMax.value * uni.upx2px(props.width))
    }
})

function zhuanghuaValue(){
    if(!isDablue.value){
        let vsp = Number(props.defaultValue);
        vsp = vsp>=_valueMax.value?_valueMax.value:vsp;
        vsp = vsp<=props.min?props.min:vsp;
        let vl = Math.abs(vsp / _valueMax.value * uni.upx2px(props.width));
        btnPos.value[0].x = vl;
    }else{
         let vsp_0 = Number(props.defaultValue[0]);
        vsp_0 = vsp_0>=_valueMax.value?_valueMax.value:vsp_0;
        vsp_0 = vsp_0<=props.min?props.min:vsp_0;
        let vl_0 = Math.abs(vsp_0 / _valueMax.value * uni.upx2px(props.width))
        btnPos.value[0].x =vl_0

         let vsp_1 = Number(props.defaultValue[1]);
        vsp_1 = vsp_1>=_valueMax.value?_valueMax.value:vsp_1;
        vsp_1 = vsp_1<=props.min?props.min:vsp_1;
        let vl_1 = Math.abs(vsp_1 / _valueMax.value * uni.upx2px(props.width))
        btnPos.value[1].x =vl_1

    }
}

function getValue(){
    if(!isDablue.value){
       return Math.ceil(Math.abs(btnPos.value[0].x)/uni.upx2px(props.width) * _valueMax.value + props.min);
    }else{
        return [
            Math.ceil(Math.abs(btnPos.value[0].x)/uni.upx2px(props.width) * _valueMax.value + props.min),
            Math.ceil(Math.abs(btnPos.value[1].x)/uni.upx2px(props.width) * _valueMax.value + props.min)
        ]
    }
}


function butnMoveStart(e:btnMovetype,index:number){
    if(props.disabled) return
    isDarg.value = true;
    
   if( props.direction == 'horizontal'){
       _x = e.x - btnPos.value[index].x;
   }else{
       _x = e.y - btnPos.value[index].x;
   }
   
    BtnIndex.value = index;
}
function butnMove(e:btnMovetype,index:number){
    if(props.disabled) return
    if(!isDarg.value) return;
    let left = e.x-_x;
    if( props.direction != 'horizontal'){
       left = e.y-_x;
   }
    if(left < buttonStaticsLeft) {
        left = buttonStaticsLeft
    }else if(left > buttonStaticsMaxLeft) {
        left = buttonStaticsMaxLeft
    }
    
    btnPos.value[index].x = left
    showDetail.value = true;
    getDomRectBound()
}
function butnMoveEnd(e:btnMovetype,index:number){
    if(props.disabled) return
    isDarg.value = false
    showDetail.value = false;
    emits("update:modelValue",getValue())
    emits("change",getValue())
	pushFormItem()
}

function getDomRectBound(){
    // #ifdef APP-NVUE
    dom.getComponentRect(proxy?.$refs['btn'+BtnIndex.value], function (res) {
        if (res?.size) {
            // { ...res.size }
            // console.log(res.size)
            const { left,top } = res.size
            nvueDetailPos.value = {
                left:left - (uni.upx2px(100) - uni.upx2px(props.buttonSize))/2,
                bottom:top-45,
            }
        }
    })
    // #endif
}





/** -----------form专有------------ */
//父级方法。
const rulesObj = inject("tmFormItemRules",computed<Array<rulesItem>>(()=>{
    return [
        {
            message:"请选择",
            required:false,
            validator:false
        }
    ]
}))
//父级方法。
let parentFormItem:any = proxy?.$parent
while (parentFormItem) {
    if (parentFormItem?.tmFormComnameFormItem == 'tmFormComnameFormItem' || !parentFormItem) {
        break;
    } else {
        parentFormItem = parentFormItem?.$parent ?? undefined
       
    }
}
const validate =(rules:Array<rulesItem>)=>{
    rules = rules.map(el=>{
        if(typeof el.validator === "function" && el.required===true){
            return el
        }else if(typeof el.validator === "boolean" && el.required===true){
            return {
                ...el,
                validator:(val:string|number|Array<string|number>)=>{
                    if(Array.isArray(val)){
                        return val.reduce((a,b)=>Number(a)+Number(b))==0
                    }else{
                        return Number(val)==0
                    }
                }
            }
        }else{
            return {
                ...el,
                validator:(val:string|number)=>{
                    return true
                }
            }
        }
        
    })
    let rules_filter:Array<rulesItem> = rules.filter(el=>{
        return typeof el.validator === "function" && el.required===true
    })
    let _valueSlider = getValue();
    let rules_fun:Array<Promise<rulesItem>> = rules_filter.map(el=>{
        return new Promise(async (res,rej)=>{
            if(typeof el.validator ==='function'){
                let vr = await el.validator(_valueSlider)
                if(vr){
                    res({
                        message:String(el.message),
                        validator:true
                    })
                }else{
                    rej({
                        message:el.message,
                        validator:false
                    })
                }
            }else{
                res({
                    message:el.message,
                    validator:true
                })
            }
        })
    })
    return Promise.all(rules_fun)
}

async function pushFormItem(isCheckVail = true){
    if (parentFormItem) {
        if (isCheckVail) {
            let _valueSlider = getValue();
            validate(toRaw(rulesObj.value)).then(ev => {
                parentFormItem.pushCom({
                    value: _valueSlider,
                    isRequiredError: false,//true,错误，false正常 检验状态
                    componentsName: 'tm-rate',//表单组件类型。
                    message: ev.length==0?"":ev[0].message,//检验信息提示语。
                })
            }).catch(er => {
                parentFormItem.pushCom({
                    value: _valueSlider,
                    isRequiredError: true,//true,错误，false正常 检验状态
                    componentsName: 'tm-slider',//表单组件类型。
                    message: er.message,//检验信息提示语。
                })
                
            })
        }
    }
    
}
pushFormItem()

const tmFormFun = inject("tmFormFun",computed(()=>""))
watch(tmFormFun,()=>{
    if(tmFormFun.value=='reset'){
		if(isDablue.value){
			emits('update:modelValue',[0,0])
			btnPos.value[0].x = 0
			btnPos.value[1].x = 0
		}else{
			emits('update:modelValue',0)
			btnPos.value[0].x = 0
		}
		pushFormItem(false)
    }
})

/** -----------end------------ */




</script>
<style scoped>

</style>